package controllers

import (
	"../../../deps/lessgo/pagelet"
	"../../../deps/lessgo/utils"
	"../../base"
	"../../net"
	"../../state"
	"../../store"
	"io"
	"regexp"
	"strconv"
	"strings"
)

var (
	dataKeysPreLen = len("data/keys")
	keyFilterReg   = regexp.MustCompile("/+")
)

type Data struct {
	*pagelet.Controller
	bucket    string
	accessKey string
	signKey   string
	rsp       map[string]interface{}
}

func (c Data) FeedAction() {

	c.AutoRender = false

	c.rsp = map[string]interface{}{}

	defer func() {
		if rspj, err := utils.JsonEncode(c.rsp); err == nil {
			io.WriteString(c.Response.Out, rspj)
		}
	}()

	c.Params.Set("key", c.Params.Get("keys"))

	switch c.Request.Method {
	case "GET":
		c.dataKeysHandler("GETS")
	default:
		c.rsp["errorCode"] = 100
	}
}

func (c Data) KeysAction() {

	c.AutoRender = false

	c.rsp = map[string]interface{}{}

	defer func() {
		if rspj, err := utils.JsonEncode(c.rsp); err == nil {
			io.WriteString(c.Response.Out, rspj)
		}
	}()

	if len(c.Request.RequestPath) < dataKeysPreLen {
		return
	}

	key := c.Request.RequestPath[dataKeysPreLen:]

	bucket := strings.ToLower(strings.Trim(keyFilterReg.ReplaceAllString(key, "/"), "/"))
	if i := strings.Index(bucket, "/"); i > 0 {
		c.bucket = bucket[0:i]
	} else {
		c.bucket = bucket
	}

	auth := c.Request.Header.Get("Authorization")
	if len(auth) > 20 && auth[:4] == "LOS " {
		if ks := strings.Split(auth[4:], ":"); len(ks) == 2 {
			c.accessKey = ks[0]
			c.signKey = ks[1]
		}
	}

	var aKey base.BucketAccessKey
	store.Cmd("HGET", store.NodeData+"sys/bak/"+c.accessKey, store.NodeSepVal).Json(&aKey)
	if aKey.Id == "" || aKey.Bucket != c.bucket || aKey.Status != 1 {
		c.rsp["errorCode"], c.rsp["message"] = 401, "Access Denied"
		return
	}
	sgKey := base.Sign(c.Request.Request, c.Request.RawBody, aKey.Secret)
	if c.signKey != sgKey {
		c.rsp["errorCode"], c.rsp["message"] = 401, "Access Denied"
		return
	}

	c.Params.Set("key", key)

	switch c.Request.Method {
	case "GET":
		c.dataKeysHandler("GET")
	case "POST", "PUT":
		c.dataKeysHandler("SET")
	case "DELETE":
		c.dataKeysHandler("DEL")
	default:
		c.rsp["errorCode"] = 100
	}

	//fmt.Println("key", len(c.Request.RequestPath), c.Request.RequestPath, key)
}

func (c Data) dataKeysHandler(method string) {

	var rsp *base.Reply

	node := base.DataNode{
		Key:   c.Params.Get("key"),
		Value: c.Params.Get("value"),
		Ttl:   -1,
	}
	if c.Params.Get("prevValue") != "" {
		node.PrevValue = c.Params.Get("prevValue")
	}
	if c.Params.Get("ttl") != "" {
		if ttl, _ := strconv.Atoi(c.Params.Get("ttl")); ttl > 0 {
			node.Ttl = ttl
		}
	}
	nodej, _ := utils.JsonEncode(node)

	req := base.PeerRequest{
		Method: method,
		Body:   nodej,
	}

	call := net.NewNetCall()
	call.Method = "Proposer.Cmd"
	call.Addr = state.Leader.Addr + ":" + state.Leader.Port
	call.Args = req
	call.Reply = new(base.Reply)

	//fmt.Println("call", call, call.Addr)

	net.RpcInstance.Call(call)

	// TODO timeout
	st := <-call.Status
	close(call.Status)

	rsp = call.Reply.(*base.Reply)
	//fmt.Println(rsp)

	if st == 9 {
		rsp.Type = base.ReplyTimeout
	}

	if rsp.Err != nil {
		rsp.Type = base.ReplyError
	}

	switch method {
	case "GET":
		var storeNode store.Node
		if err := rsp.Json(&storeNode); err == nil && storeNode.R > 0 {
			c.rsp["action"] = "get"

			rnode := map[string]interface{}{}

			rnode["key"] = storeNode.P
			rnode["version"] = storeNode.R

			if storeNode.T == store.NodeTypeKey {
				rnode["value"] = storeNode.C
			}

			if storeNode.L > 0 {
				rnode["ttl"] = storeNode.L
			}

			if storeNode.T == store.NodeTypeDir {
				rnode["dir"] = true

				snodes := []map[string]interface{}{}
				for _, v := range storeNode.N {

					snode := map[string]interface{}{
						"key":     v.P,
						"version": v.R,
					}

					if v.T == store.NodeTypeDir {
						snode["dir"] = true
					}

					snodes = append(snodes, snode)
				}

				rnode["nodes"] = snodes
			}

			c.rsp["node"] = rnode

		} else {
			c.rsp["errorCode"] = 100
			c.rsp["message"] = "Key Not Found"
		}
	case "GETS":

		if rsp.Err == nil {

			var nodes []store.Node
			if err := rsp.Json(&nodes); err == nil {

				c.rsp["action"] = "gets"

				data := []map[string]interface{}{}

				for _, v := range nodes {

					data = append(data, map[string]interface{}{
						"key":     v.P,
						"value":   v.C,
						"version": v.R,
					})
				}

				c.rsp["data"] = data
			}
		}

		if c.rsp["action"] == "" {
			c.rsp["errorCode"] = 100
			c.rsp["message"] = "Keys Not Found"
		}

	case "SET", "DEL":
		if rsp.Err == nil {
			c.rsp["action"] = strings.ToLower(method)
			var node base.DataNode
			err := rsp.Json(&node)
			if err == nil {
				if method == "DEL" {
					node.Value = ""
				}
				c.rsp["node"] = node
			}
		} else {
			c.rsp["errorCode"] = 100
		}
	}

}
